﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NModbus;
using RotovapPlatform.Domain.Common.Exceptions;
using RotovapPlatform.Domain.Equipment.Configurations;
using RotovapPlatform.Domain.Equipment.Interfaces.Communication;
using System.Threading;
using System.IO;
using System.Linq;

namespace RotovapPlatform.Infrastructure.Equipment.Communication.SerialServer
{
	public class SerialServerManager : ISerialServerManager
	{
		private readonly Dictionary<int, TcpClient> _clients = new();
		private readonly Dictionary<int, IModbusMaster> _masters = new();
		private readonly Dictionary<int, bool> _portConnectionStatus = new();
		private readonly Dictionary<int, DateTime> _lastReconnectAttempt = new();
		private readonly ILogger<SerialServerManager> _logger;
		private readonly SerialServerConfig _config;
		private bool _isConnected;
		private readonly SemaphoreSlim _communicationLock = new SemaphoreSlim(1, 1);
		private readonly TimeSpan _reconnectInterval = TimeSpan.FromSeconds(5); // 重连间隔

		public SerialServerManager(
			SerialServerConfig config,
			ILogger<SerialServerManager> logger)
		{
			_config = config;
			_logger = logger;

			// 初始化端口连接状态和重连时间
			foreach (var port in _config.DevicePorts)
			{
				_portConnectionStatus[port] = false;
				_lastReconnectAttempt[port] = DateTime.MinValue;
			}
		}

		/// <summary>
		/// 获取指定端口的连接状态
		/// </summary>
		public bool IsPortConnected(int port)
		{
			return _portConnectionStatus.TryGetValue(port, out bool status) && status;
		}

		/// <summary>
		/// 判断整体连接状态，任一端口连接成功则认为已连接
		/// </summary>
		public bool IsConnected => _isConnected;

		public async Task<bool> ConnectAsync()
		{
			_logger.LogInformation("开始连接串口服务器所有端口");

			bool anyConnected = false;

			foreach (var port in _config.DevicePorts)
			{
				bool success = await ConnectPortAsync(port);
				if (success)
				{
					anyConnected = true;
				}
			}

			// 更新总体连接状态
			_isConnected = anyConnected;

			if (anyConnected)
			{
				_logger.LogInformation("串口服务器至少有一个端口连接成功");
			}
			else
			{
				_logger.LogWarning("串口服务器所有端口连接都失败");
			}

			return anyConnected;
		}

		/// <summary>
		/// 连接单个端口
		/// </summary>
		private async Task<bool> ConnectPortAsync(int port)
		{
			try
			{
				// 如果已连接，先断开
				if (_clients.TryGetValue(port, out var existingClient))
				{
					try
					{
						existingClient.Close();
						existingClient.Dispose();
						_clients.Remove(port);

						// 同时清理Modbus主站
						if (_masters.TryGetValue(port, out var existingMaster))
						{
							(existingMaster as IDisposable)?.Dispose();
							_masters.Remove(port);
						}
					}
					catch (Exception ex)
					{
						_logger.LogWarning(ex, $"断开端口 {port} 旧连接时发生错误");
					}
				}

				// 设置连接超时时间
				using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

				var client = new TcpClient();
				_logger.LogInformation($"正在连接串口服务器端口 {port}...");

				// 尝试连接，带超时
				var connectTask = client.ConnectAsync(_config.IpAddress, port);
				await Task.WhenAny(connectTask, Task.Delay(5000, cts.Token));

				if (!client.Connected)
				{
					_logger.LogWarning($"端口 {port} 连接超时或失败");
					_portConnectionStatus[port] = false;
					return false;
				}

				// 创建Modbus主站
				var factory = new ModbusFactory();
				var master = factory.CreateMaster(client);

				// 保存连接和主站实例
				_clients[port] = client;
				_masters[port] = master;
				_portConnectionStatus[port] = true;

				_logger.LogInformation($"串口服务器端口 {port} 连接成功");
				return true;
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, $"连接端口 {port} 时发生错误");
				_portConnectionStatus[port] = false;
				return false;
			}
		}

		public async Task DisconnectAsync()
		{
			await _communicationLock.WaitAsync();
			try
			{
				if (!_isConnected) return;

				foreach (var master in _masters.Values)
				{
					try
					{
						(master as IDisposable)?.Dispose();
					}
					catch (Exception ex)
					{
						_logger.LogError(ex, "关闭Modbus主站时发生错误");
					}
				}

				foreach (var client in _clients.Values)
				{
					try
					{
						client.Close();
						client.Dispose();
					}
					catch (Exception ex)
					{
						_logger.LogError(ex, "关闭TCP客户端时发生错误");
					}
				}

				// 更新所有端口状态
				foreach (var port in _portConnectionStatus.Keys.ToList())
				{
					_portConnectionStatus[port] = false;
				}

				_masters.Clear();
				_clients.Clear();
				_isConnected = false;

				_logger.LogInformation("所有串口服务器连接已断开");
			}
			finally
			{
				_communicationLock.Release();
			}
		}

		/// <summary>
		/// 尝试重新连接指定端口
		/// </summary>
		private async Task<bool> TryReconnectPortAsync(int port)
		{
			// 控制重连频率，避免频繁重连
			if (DateTime.Now - _lastReconnectAttempt.GetValueOrDefault(port, DateTime.MinValue) < _reconnectInterval)
			{
				return false;
			}

			_lastReconnectAttempt[port] = DateTime.Now;
			_logger.LogInformation($"尝试重新连接串口服务器端口 {port}...");

			bool reconnectSuccess = await ConnectPortAsync(port);

			// 更新全局连接状态
			UpdateGlobalConnectionStatus();

			return reconnectSuccess;
		}

		/// <summary>
		/// 更新全局连接状态标志
		/// </summary>
		private void UpdateGlobalConnectionStatus()
		{
			bool oldConnectedState = _isConnected;
			_isConnected = _portConnectionStatus.Values.Any(connected => connected);

			// 如果连接状态发生变化，记录日志
			if (oldConnectedState != _isConnected)
			{
				if (_isConnected)
					_logger.LogInformation("串口服务器连接状态已恢复");
				else
					_logger.LogWarning("串口服务器所有连接均已断开");
			}
		}

		public async Task<ushort[]> ReadHoldingRegistersAsync(int devicePort, byte slaveId, ushort startAddress, ushort length)
		{
			bool lockTaken = false;
			try
			{
				// 获取锁，设置标志
				await _communicationLock.WaitAsync();
				lockTaken = true;

				// 检查并获取主站，如果断开则尝试重连
				IModbusMaster master = await EnsurePortConnectedAsync(devicePort);

				// 设置较短的超时时间，避免长时间阻塞
				int originalTimeout = master.Transport.ReadTimeout;
				try
				{
					// 实际设备可能需要更长的响应时间，根据实际设备调整
					master.Transport.ReadTimeout = 1000; // 1秒超时
					return await master.ReadHoldingRegistersAsync(slaveId, startAddress, length);
				}
				finally
				{
					// 还原原始超时设置
					master.Transport.ReadTimeout = originalTimeout;
				}
			}
			catch (TimeoutException timeoutEx)
			{
				// 超时异常特殊处理，标记端口为断开状态
				_logger.LogWarning(timeoutEx, $"读取保持寄存器超时: 端口={devicePort}, 从站={slaveId}");
				_portConnectionStatus[devicePort] = false;

				// 更新全局连接状态
				UpdateGlobalConnectionStatus();

				throw new CommunicationException($"读取保持寄存器超时: 端口={devicePort}, 从站={slaveId}", timeoutEx);
			}
			catch (IOException ioEx)
			{
				// IO异常特殊处理，可能是设备断开连接
				_logger.LogWarning(ioEx, $"读取保持寄存器IO错误: 端口={devicePort}, 从站={slaveId}");
				_portConnectionStatus[devicePort] = false;

				// 更新全局连接状态
				UpdateGlobalConnectionStatus();

				throw new CommunicationException($"读取保持寄存器IO错误: 端口={devicePort}, 从站={slaveId}", ioEx);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, $"读取保持寄存器失败: 端口={devicePort}, 从站={slaveId}");
				throw new CommunicationException($"读取保持寄存器失败: 端口={devicePort}, 从站={slaveId}", ex);
			}
			finally
			{
				// 确保锁被释放
				if (lockTaken)
				{
					_communicationLock.Release();
				}
			}
		}

		public async Task WriteHoldingRegistersAsync(int devicePort, byte slaveId, ushort startAddress, ushort[] values)
		{
			try
			{
				await _communicationLock.WaitAsync();
				try
				{
					// 检查并获取主站，如果断开则尝试重连
					IModbusMaster master = await EnsurePortConnectedAsync(devicePort);
					await master.WriteMultipleRegistersAsync(slaveId, startAddress, values);
				}
				finally
				{
					_communicationLock.Release();
				}
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, $"写入保持寄存器失败: 端口={devicePort}");
				throw new CommunicationException("写入保持寄存器失败", ex);
			}
		}

		public async Task<bool[]> ReadCoilsAsync(int devicePort, byte slaveId, ushort startAddress, ushort length)
		{
			try
			{
				await _communicationLock.WaitAsync();
				try
				{
					// 检查并获取主站，如果断开则尝试重连
					IModbusMaster master = await EnsurePortConnectedAsync(devicePort);
					return await master.ReadCoilsAsync(slaveId, startAddress, length);
				}
				finally
				{
					_communicationLock.Release();
				}
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, $"读取线圈失败: 端口={devicePort}");
				throw new CommunicationException("读取线圈失败", ex);
			}
		}

		public async Task WriteCoilsAsync(int devicePort, byte slaveId, ushort startAddress, bool[] values)
		{
			try
			{
				await _communicationLock.WaitAsync();
				try
				{
					// 检查并获取主站，如果断开则尝试重连
					IModbusMaster master = await EnsurePortConnectedAsync(devicePort);
					await master.WriteMultipleCoilsAsync(slaveId, startAddress, values);
				}
				finally
				{
					_communicationLock.Release();
				}
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, $"写入线圈失败: 端口={devicePort}");
				throw new CommunicationException("写入线圈失败", ex);
			}
		}

		public async Task<byte[]> SendCustomCommandAsync(int devicePort, byte[] command, int expectedResponseLength, int timeoutMs = 1000)
		{
			await _communicationLock.WaitAsync();
			try
			{
				// 检查并获取客户端连接，如果断开则尝试重连
				TcpClient client = await EnsureClientConnectedAsync(devicePort);

				// 设置读写超时
				client.ReceiveTimeout = timeoutMs;
				client.SendTimeout = timeoutMs;

				// 获取网络流
				var stream = client.GetStream();

				// 发送命令
				_logger.LogTrace($"发送自定义命令: {BitConverter.ToString(command)}");
				await stream.WriteAsync(command, 0, command.Length);
				await stream.FlushAsync();

				// 接收响应
				byte[] responseBuffer = new byte[expectedResponseLength];
				int bytesRead = 0;
				int totalBytesRead = 0;

				// 使用CancellationToken实现超时
				using var cts = new CancellationTokenSource(timeoutMs);

				// 创建读取任务
				var readTask = Task.Run(async () => {
					try
					{
						while (totalBytesRead < expectedResponseLength)
						{
							bytesRead = await stream.ReadAsync(responseBuffer, totalBytesRead,
								expectedResponseLength - totalBytesRead, cts.Token);

							if (bytesRead == 0)
								break; // 连接已关闭

							totalBytesRead += bytesRead;
						}
					}
					catch (OperationCanceledException)
					{
						_logger.LogWarning($"读取响应超时: 期望 {expectedResponseLength} 字节, 实际读取 {totalBytesRead} 字节");
					}
					return totalBytesRead;
				});

				try
				{
					await readTask;
				}
				catch (TaskCanceledException)
				{
					_logger.LogWarning("读取响应任务被取消");
				}

				if (totalBytesRead < expectedResponseLength)
				{
					_logger.LogWarning($"响应数据不完整: 期望 {expectedResponseLength} 字节, 实际读取 {totalBytesRead} 字节");
				}

				// 复制实际读取的数据
				byte[] actualResponse = new byte[totalBytesRead];
				Array.Copy(responseBuffer, actualResponse, totalBytesRead);

				_logger.LogTrace($"接收到响应: {BitConverter.ToString(actualResponse)}");
				return actualResponse;
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, $"发送自定义命令失败: 端口={devicePort}");
				throw new CommunicationException("发送自定义命令失败", ex);
			}
			finally
			{
				_communicationLock.Release();
			}
		}

		/// <summary>
		/// 确保端口已连接并返回有效的Modbus主站
		/// </summary>
		private async Task<IModbusMaster> EnsurePortConnectedAsync(int devicePort)
		{
			IModbusMaster master;

			// 检查端口连接字典是否包含该端口
			if (!_portConnectionStatus.TryGetValue(devicePort, out var connection) || !connection)
			{
				_logger.LogWarning($"端口 {devicePort} 未连接或标记为断开状态，尝试建立连接");
				await ConnectPortAsync(devicePort);
			}

			// 重新获取连接状态
			connection = _portConnectionStatus[devicePort];

			// 检查是否有可用的master
			if (!_masters.TryGetValue(devicePort, out master))
			{
				_logger.LogWarning($"端口 {devicePort} 无可用的Modbus主站，尝试重新连接");
				await ConnectPortAsync(devicePort);
				if (!_masters.TryGetValue(devicePort, out master))
				{
					throw new CommunicationException($"端口 {devicePort} 无法获取Modbus主站");
				}
			}

			//// 额外的连接验证：尝试进行一个简单的通信测试
			//try
			//{
			//	// 执行一个简单的读取操作以验证连接可用性
			//	// 使用较短的超时时间，避免长时间等待
			//	var originalTimeout = master.Transport.ReadTimeout;
			//	try
			//	{
			//		// 设置短超时时间进行测试，避免长时间阻塞
			//		master.Transport.ReadTimeout = 500; // 500ms超时

			//		// 尝试读取第一个保持寄存器，通常设备总会有某些寄存器可以读取
			//		// 使用默认的从站地址0，如有需要可以修改
			//		// 这里只是验证通信，结果并不重要
			//		await master.ReadHoldingRegistersAsync(1, 0, 1);

			//		// 标记设备为已连接状态
			//		_portConnectionStatus[devicePort] = true;
			//		return master;
			//	}
			//	finally
			//	{
			//		// 还原原始超时设置
			//		master.Transport.ReadTimeout = originalTimeout;
			//	}
			//}
			//catch (Exception ex)
			//{
			//	_logger.LogWarning(ex, $"端口 {devicePort} TCP连接已建立，但通信测试失败，将重新建立连接");
			//	_portConnectionStatus[devicePort] = false;

			//	// 关闭现有连接
			//	try
			//	{
			//		if (_clients.TryGetValue(devicePort, out var client))
			//		{
			//			client.Close();
			//			client.Dispose();
			//			_clients.Remove(devicePort);
			//		}
			//		if (_masters.TryGetValue(devicePort, out var oldMaster))
			//		{
			//			(oldMaster as IDisposable)?.Dispose();
			//			_masters.Remove(devicePort);
			//		}
			//	}
			//	catch (Exception disposeEx)
			//	{
			//		_logger.LogWarning(disposeEx, $"关闭端口 {devicePort} 连接时发生错误");
			//	}

			//	// 尝试重新建立连接
			//	await ConnectPortAsync(devicePort);

			//	// 重新获取连接对象
			//	if (!_masters.TryGetValue(devicePort, out master))
			//	{
			//		throw new CommunicationException($"端口 {devicePort} 重连后仍无法获取Modbus主站");
			//	}
			//	return master;
			//}

			return master;
		}

		/// <summary>
		/// 确保端口已连接并返回有效的TcpClient
		/// </summary>
		private async Task<TcpClient> EnsureClientConnectedAsync(int devicePort)
		{
			// 检查是否有连接
			if (!_clients.TryGetValue(devicePort, out var client) || !client.Connected)
			{
				bool reconnectSuccess = await TryReconnectPortAsync(devicePort);
				if (!reconnectSuccess || !_clients.TryGetValue(devicePort, out client))
				{
					throw new CommunicationException($"端口 {devicePort} 未连接且重连失败");
				}
			}

			return client;
		}

		/// <summary>
		/// 检查所有连接状态，并尝试重连断开的连接
		/// </summary>
		public async Task<bool> CheckConnectionAsync()
		{
			await _communicationLock.WaitAsync();
			try
			{
				List<int> disconnectedPorts = new List<int>();

				// 检查每个连接的状态
				foreach (var portEntry in _portConnectionStatus.ToList())
				{
					int port = portEntry.Key;
					bool isConnected = portEntry.Value;

					if (!isConnected ||
						!_clients.TryGetValue(port, out var client) ||
						!client.Connected)
					{
						disconnectedPorts.Add(port);
						_portConnectionStatus[port] = false;
					}
				}

				// 尝试重连断开的端口
				bool allReconnected = true;
				foreach (int port in disconnectedPorts)
				{
					if (!await TryReconnectPortAsync(port))
					{
						allReconnected = false;
					}
				}

				// 更新全局连接状态
				UpdateGlobalConnectionStatus();

				return allReconnected && disconnectedPorts.Count == 0;
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "检查连接状态时发生错误");
				return false;
			}
			finally
			{
				_communicationLock.Release();
			}
		}
	}
}